home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / lang_c / cug191 / clink.c < prev    next >
C/C++ Source or Header  |  1986-07-10  |  17KB  |  496 lines

  1. /* CLINK       6-9-85 ported to Mix C  5-31-86       */
  2. /* Linkage of C source code subroutines              */
  3. /* Phillip L. Emerson    Cleveland State University  */
  4.  
  5.       /* some modifiable parameters */
  6. #define SAME 0
  7. #define LESS -1
  8. #define MORE 1
  9. #define ONE 1
  10. #define VOID -1
  11. #define UNDRSC '\137'   /* ASCII special characters */
  12. #define BELL '\007'
  13. #define APOSTR '\047'
  14. #define QUOTE '\042'
  15. #define BACKSL '\134'
  16. #define LF '\n'
  17. #define SP '\40'
  18. #define BUFSZ 5000       /*must hold any lib. func.  */
  19. #define NAMSIZ 20        /*max length of func. names */
  20. #define HASHSIZ 512      /*size of hash table        */
  21. #define LINSIZ 140       /*source-code max line  ln  */
  22. #define FNAMSZ 20        /*max len. of file names    */
  23. #define OFFSET 20        /*buff offset for backup    */
  24. #define MARGA 10         /*error margin for buff ovfl */
  25. #define MARGB 15         /*error margin for buff ovfl */
  26. #include "stdio"
  27. /*$SIGNEXT*/
  28. /*$ZERO*/
  29. /* Version for Mix C.--  Main changes from C/80 version:
  30.  1. added #include "stdio"
  31.  2. added above two $ comments as Mix compiler parameters
  32.  3. removed #define NULL, which is done in Mix's stdio
  33.  4. changed BUFSIZ to BUFSZ ; BUFSIZ used by Mix's stdio
  34.  5. changed IO type from int to FILE *
  35.  6. changed erroneous instance of linebuff to linebuf
  36.  7. removed #define alloc sbrk & used calloc() instead
  37. */
  38. char progfil[FNAMSZ],libfil[FNAMSZ],keyfil[FNAMSZ],outfil[FNAMSZ];
  39. char *fubuff, *linebuf ;
  40. FILE *prchan,*lichan,*keychan,*ouchan,*fopen(),*foptst();
  41. int quotes,apostrs,combal;
  42. char *calloc() ;
  43. /* some functs. before main */
  44.  
  45. /* next f name into r, from string starting
  46.    at p -- return 0 if end reached */
  47. char *funam(p,r) char *p,*r; {char *t,*q,*s,*u; int n;
  48.   u = r ;
  49. loop: s = p; r = u ;
  50.    while ((*p) && (*p != '(' )) p++ ;
  51.    if (*p == NULL) return(NULL) ;
  52.    t = p-- ;
  53.    while ((*p == ' ' ) && (p > s)) p-- ;
  54.    q = p ;
  55.    if( !lglname(*q)) { p = ++t ; goto loop ; }
  56.    while ( lglname(*q) && (q >= s)) q-- ;
  57.    q++ ;
  58.    for(n=1; (n < (NAMSIZ-1)) && (q <= p); *r++ = *q++,n++) ;
  59.    *r = NULL;
  60.    if ((q < s) || (n >= (NAMSIZ-1)) || (n <= 0))
  61.       {putstr("error in processing name\n") ;
  62.        putstr(u); putchar(LF); exit(); }
  63.    if (systn(u)) { p = ++t; goto loop; }
  64.    return(++t) ; }
  65.  
  66. /* list of commen system function names to ignore */
  67. #define SNAMES 19  /* nr of names*/
  68. char *sysnam(n) int n;
  69. {static char *name[] = {
  70. "if","for","while","fclose","fopen","getc",
  71. "putc","write","read","getchar","putchar",
  72. "alloc","return","main","seek","ftell",
  73. "ftellr","exec","switch" } ; return(name[n]) ; }
  74.  
  75. /* Mod2 counting of apostrophes and quotes */
  76. char quotap(n) char n; {static char last;
  77.   if(( n == QUOTE) && (last != BACKSL ) && !apostrs)
  78.      quotes = ((quotes+1) % 2) ;
  79.   if(( n == APOSTR) && (last != BACKSL) && !quotes)
  80.      apostrs=((apostrs+1) % 2) ;
  81.   last = n ;
  82.   return(n) ; }
  83.  
  84. /* allocate space for s, store s, and return ptr to it */
  85. char *strsave(s) char *s; {char *p ;
  86.    if (((p=calloc(1,strlen(s)+1)) == VOID) || (p == NULL))
  87.           return(NULL) ;
  88.    strcpy(p,s); return(p) ; }
  89.  
  90. /* search for string q on p. rtrn ptr to it if so, null if not*/
  91. char *strison(p,q) char *p,*q; {
  92.    for( ; *p ; p++) {if(strisat(p,q)) return(p) ; }
  93.    return(NULL) ; }
  94.  
  95. /* standard getch & ungetch to be used in next routine*/
  96. int getctr ;
  97. char getcbuf[5] ;
  98. char getch()
  99.   { if(getctr) return (getcbuf[--getctr]); return(getc(lichan)) ; }
  100. ungetch(a) char a;
  101.   {getcbuf[getctr++] = a; return; }
  102.  
  103. /* get a char from libchan, ignoring literals & comments */
  104. char gignor()
  105. { char n,m;
  106. loop1: if ((n=getch()) == VOID) return(VOID) ;
  107.    if((n != '/') || quotes || apostrs) return(quotap(n)) ;
  108.    if((m=getch()) != '*') {ungetch(m) ; return(quotap(n)) ; }
  109. loop2: while (((m=getch()) != '*') && (m != VOID))
  110.         {n=getch(); ungetch(n);
  111.          if((m == '/') && (n == '*')) combal++ ; }
  112.    if (m == VOID) return(VOID) ;
  113.    if((m=getch()) != '/') {ungetch(m) ; goto loop2; }
  114.    goto loop1; }
  115.  
  116. oulab() {putl("/* file ",ouchan);putl(outfil,ouchan);putl(" */\n",ouchan);}
  117.  
  118. struct list {
  119. char *hashkey ;
  120. char *defntn ;
  121. struct list *next ; } ;
  122. struct list **table1 ;
  123. struct list **table2 ;
  124.  
  125. /*  BEGINNING OF MAIN PROGRAM */
  126. main(argc,argv) int argc; char *argv[];
  127. {
  128.   putstr("CLINK  v. 6-9-85 ported to Mix C 5-31-86\n") ;
  129.   linebuf = calloc(1,LINSIZ) ;
  130.   table1 = (struct list **) calloc(1,HASHSIZ*(sizeof(struct list *))) ;
  131.   table2 = (struct list **) calloc(1,HASHSIZ*(sizeof(struct list *))) ;
  132.   tabinit() ;
  133.   fubuff = calloc(1,BUFSZ) ;
  134.   if( (fubuff == NULL) || (fubuff == VOID))
  135.     {putstr("not enough room for buffer\n"); exit(); }
  136.   strcpyn(progfil,argv[1],FNAMSZ-1) ;
  137.   strcpyn(libfil,argv[2],FNAMSZ-1) ;
  138.   strcpyn(keyfil,argv[3],FNAMSZ-1) ;
  139.   strcpyn(outfil,argv[4],FNAMSZ-1) ;
  140.   if (argc >= 2)
  141.       { if ( !strison(progfil,"."))
  142.             strcpy(progfil+strlen(progfil),".B") ;
  143.         if (argc < 5) strcpyc(outfil,progfil,'.') ;
  144.         if (argc < 4) strcpy(keyfil,"LIBKEY.B") ;
  145.         if (argc < 3) strcpy(libfil,"USRLIB.B") ;
  146.         defalt(progfil,keyfil,".B") ;
  147.         defalt(progfil,libfil,".B") ;
  148.         defalt(progfil,outfil,".C") ;
  149.         putchar(LF) ;
  150.         putstr(progfil) ; putstr(" program file\n") ;
  151.         putstr(libfil) ; putstr(" library file\n") ;
  152.         putstr(keyfil) ; putstr(" library key file\n") ;
  153.         putstr(outfil) ; putstr(" output file \n") ;
  154.         putstr("press RETURN (or ENTER) to proceed, Ctrl-C to abort\n") ;
  155.         getchar() ;
  156.         hashup() ;
  157.         prchan = foptst(progfil,"r") ;
  158.         ouchan = foptst(outfil,"w") ;
  159.         oulab() ;
  160.         buildup(1) ;
  161.      }
  162.  else
  163.      {  putstr("\nlibrary file name? ") ;  getstr(libfil) ;
  164.         putstr("\nlibrary keys file name? "); getstr(keyfil) ;
  165.         putstr("wait -- getting library keys\n") ;
  166.         hashup() ;
  167.         putstr("output file name? ") ;  getstr(outfil) ;
  168.         ouchan = foptst(outfil,"w") ; oulab() ;
  169.         for (*progfil='1'; (*progfil) && (*progfil != LF) ; )
  170.           { putstr("input file name? -- ") ;
  171.             putstr("just press RETURN (or ENTER) if no more ") ;
  172.             *progfil = NULL; getstr(progfil) ;
  173.             if ( (*progfil) && (*progfil != LF ) )
  174.               {prchan = foptst(progfil,"r") ;
  175.                putstr("include in output file? (type 0 for no, 1 for yes)\n");
  176.                buildup(getint()) ;
  177.               }
  178.           }
  179.      }
  180.  tidyup() ;
  181.  if (quotes || apostrs || combal)
  182.      {  putchar(LF); putstr("quotes mod2: ") ; printd(quotes) ;
  183.         putchar(LF); putstr("apostrophes mod2: "); printd(apostrs) ;
  184.         putchar(LF) ;
  185.         putstr("occurrences of comment delimiters within comments: ") ;
  186.         printd(combal) ; putchar(LF) ;
  187.      }
  188.  putstr("Linked output file is "); putstr(outfil); putstr("\n\n") ;
  189. }
  190. /*  END OF MAIN PROGRAM */
  191.  
  192. /*  MANY SUBROUTINES FOLLOW -- some of the more general purpose
  193.     ones would ordinarily reside in a library, but this program
  194.     is intended to be fairly self-contained
  195. */
  196.  
  197. /* Manipulation for defaults on file names */
  198. defalt(fpr,fothr,ext) char *fpr,*fothr,*ext;
  199.   {  char *a,temp[FNAMSZ]; int n;
  200.      if( !strison(fothr,":") && (a = strison(fpr,":") ) )
  201.          {  if ( n = a - fpr)
  202.              {  strcpyn(temp,fpr,n+1) ;
  203.                 strcpy(temp+n+1,fothr) ;
  204.                 strcpy(fothr,temp) ;
  205.              }
  206.          }
  207.      if( !strison(fothr,".") ) strcpy(fothr+strlen(fothr),ext) ;
  208.   }
  209.  
  210. /* copy t to s up to but not including 1st instance of char c */
  211. strcpyc(s,t,c) char *s,*t,c;
  212.   { while( (*s++ = *t++) != c)  ; *(--s) = NULL ; }
  213.  
  214. /* tabinit,hash,look,insert are FUNCTIONS FOR HASHING FUNCTION CALLS UNDER
  215.      FUNCTION NAMES
  216. */
  217.  
  218. tabinit()
  219.   {int n; for (n=0; n < HASHSIZ ; n++) table1[n] = table2[n]=NULL ;}
  220.  
  221. hash(s) char *s; {int val;
  222.   for (val=0; *s ;) val += *s++; return(val % HASHSIZ) ; }
  223. struct list *look(s,table) char *s; struct list **table;
  224.   {struct list *p ;
  225.    for (p=table[hash(s)] ; p ; p = p->next )
  226.      { if (strcmp(s,p->hashkey) == SAME) return(p) ; }
  227.    return(NULL) ;
  228.   }
  229. struct list *insert(name,def,table) char *name,*def ;  struct list **table;
  230.   {struct list *n; int val;
  231.    if (n=look(name,table)) return(n) ;
  232.               /* no redefinition of an old one */
  233.    n = (struct list *) calloc(1,sizeof(struct list)) ;
  234.    if ((n == VOID) || (n == NULL)) putstr("hash error#1\n") ;
  235.    if ((n->hashkey = strsave(name)) == NULL) putstr("hash error#2\n") ;
  236.    val = hash(n->hashkey) ;
  237.    n->next = table[val] ;
  238.    table[val] = n ;
  239.    if ((n->defntn = strsave(def)) == NULL) putstr("hash error#3\n") ;
  240.    return(n) ;
  241.   }
  242.  
  243. /* get byte, starting at staddr, return ptr one past byte position
  244.    in fubuff.  Return VOID if EOF.  Insert NULL at next position
  245.    after byte */
  246. char *getfrst(byte,staddr) char byte,*staddr;
  247.   { char *q;
  248.     for (q=staddr; ( (*q=gignor()) != VOID) && ((*q != byte)
  249.          || quotes || apostrs) && (q < (fubuff+BUFSZ-MARGA)) ;
  250.                  q++)  ;
  251.     if (*q == VOID) return(VOID) ;
  252.     if (q > (fubuff+BUFSZ-MARGB)) {putstr("buf ovrfl\n"); exit() ; }
  253.     *++q = NULL ; return(q) ;
  254.        /* q points one past byte position */
  255.   }
  256. /* Return 1 if c is legal in function name, 0 if not */
  257. /* assumes ASCII */
  258. lglname(c) char c ;
  259.   { if( (c >= 'a') && (c <= 'z') ) return(ONE) ;
  260.     if( (c >= 'A') && (c <= 'Z') ) return(ONE) ;
  261.     if( (c >= '0') && (c <= '9') ) return(ONE) ;
  262.     if( c == UNDRSC) return(ONE) ;
  263.     return(NULL) ;
  264.   }
  265. /* compress func. defs. to func. calls */
  266. char *cmprss(brace1) char *brace1;
  267.   { char *p,*q ;
  268.     *fubuff = '{' ;
  269.     q = fubuff; q++;
  270.     for(p=brace1; p=funam(p,q); q += strlen(q), *q++ = '(')  ;
  271.     *q++ = '}' ; *q = NULL ; redcstr(fubuff+1) ;
  272.   }
  273. /* eliminate duplications on compressed definition string */
  274. redcstr(origin) char *origin ;
  275.   { char *p,*q,*r ;
  276.     for (p=origin; *p != '}' ; p++)
  277.       { if (*p == '(' ) *p = NULL ; }
  278.     for (p=origin; *p != '}' ; )
  279.       {q=p;
  280.        while( *q != '}')
  281.          {while( *q != NULL) q++ ;
  282.           while( *q == NULL) q++ ;
  283.           if( *q != '}')
  284.              { if( strcmp(p,q) == SAME)
  285.                  for(r=q; *r != NULL; r++ ) *r = NULL ;
  286.              }
  287.          }
  288.        while( (*p != NULL) && (*p != '}') ) p++ ;
  289.        while( *p == NULL ) p++ ;
  290.       }
  291.     for (p=origin; *p != '}' ; p++)  {if (*p == NULL) *p = '(' ; }
  292.     deruns('(',origin) ;
  293.   }
  294. /* get next lib fn into fubuff, ret name, and pointer to
  295.    opening brace as value
  296. */
  297. char *getfu(name) char *name ;
  298.   {char *brace1,*p,*q,*r; int n,braces ;
  299.    for(q=fubuff+OFFSET,r=q; ((q=getfrst('(',q)) != VOID)
  300.        && (funam(r,name) == NULL) ; r=q)  ;
  301.    if(q == VOID) return(VOID) ;
  302.          /* q will point one past the '(' */
  303.    braces = NULL ;
  304.    if( (q=getfrst('{',q)) == VOID) return(VOID) ;
  305.    brace1 = --q; braces = 1 ;
  306.    while (braces > NULL)
  307.      { *++q = gignor() ;
  308.        if (*q == VOID) return(VOID) ;
  309.        if( (*q == '{') && !quotes && !apostrs) braces += 1;
  310.        if( (*q == '}') && !quotes && !apostrs) braces -= 1;
  311.      }
  312.    *++q = NULL ;
  313.    return(brace1) ;
  314.   }
  315. /* return -1 if string is among those listed as sys. funcs,
  316.    0 of not  */
  317. systn(s) char *s ;
  318.   { int k ;
  319.     for(k=0; k < SNAMES; k++)
  320.       if(strcmp(s,sysnam(k)) == SAME) return(VOID);
  321.     return(NULL) ;
  322.   }
  323. /* new hashup() 2-20-85 -- attempts to read keyfile.  If successful
  324.    ok; if not, hashes up from lib file, and puts out new keyfile */
  325. hashup()
  326.   {char *q,name[NAMSIZ]; struct list *j; int flag;
  327.    getctr=0; quotes=0; apostrs=0;
  328.    if( (flag=keyup()) == 1) return(VOID) ;
  329.    if( flag == VOID) {putstr("insert error on keys\n"); exit(); }
  330.    keychan = foptst(keyfil,"w") ;
  331.    lichan = foptst(libfil,"r") ;
  332.    while( (q=getfu(name)) != VOID)
  333.     {cmprss(q) ;
  334.      if( ( (j=insert(name,fubuff,table1)) == NULL) || (j == VOID) )
  335.        {putstr("insert error from fibfil\n") ; exit() ; }
  336.      putl(name,keychan); putc(LF,keychan) ;
  337.      putl(fubuff,keychan); putc(LF,keychan) ;
  338.     }
  339.    fclose(lichan) ;
  340.    fclose(keychan) ; return(VOID) ;
  341.   }
  342. /* tries to open keyfil for input.  If successful, reads in hash table
  343.    and returns 1.  If not, returns 0, but returns -1 if open to read
  344.    is successful and something goes wrong.  */
  345. keyup()
  346.   {struct list *j ;
  347.    keychan = fopen(keyfil,"r") ;
  348.    if( (keychan == NULL) || (keychan == VOID) )
  349.       {fclose(keychan); return(NULL) ; }
  350.    while( (getl0(linebuf,keychan) != VOID) )
  351.      {getl0(fubuff,keychan) ;
  352.       if( (j=insert(linebuf,fubuff,table1)) == NULL ) return(VOID) ;
  353.       if (j == VOID) return(VOID) ;
  354.      }
  355.    fclose(keychan); return(ONE) ;
  356.   }
  357. /* goes thru prog input file recording names of functions called; arg
  358.    =1 for input into output file, n=0 if not */
  359. buildup(n) int n;
  360.   {while(progli(n) != VOID) ; fclose(prchan) ; }
  361.  
  362. /* processes a line */
  363. progli(n) int n;
  364.   { char *p; static char r[NAMSIZ] ;
  365.     if (getl(linebuf,prchan) == VOID) return(VOID) ;
  366.     if (n) putl(linebuf,ouchan) ;
  367.     for (p=linebuf; p=funam(p,r); netup(r) ) ; return(ONE) ;
  368.   }
  369. /* gets all ramifications of a function call */
  370. /* erasing tracks to prevent endless cycling */
  371. netup(name) char *name ;
  372.   {char *p,*q,r[NAMSIZ] ;  struct list *n;
  373.    insert(name,"1",table2) ;
  374.    if( (n=look(name,table1)) == NULL ) return ;
  375.    p = q = n->defntn ;
  376.    while( p=funam(q,r) )
  377.      {*q = NULL; q = p; if( strcmp(name,r) != SAME ) netup(r) ; }
  378.    return ;
  379.   }
  380. /* goes thru lib file putting out those whose names are recorded */
  381. tidyup()
  382.   {char *p,name[NAMSIZ] ;
  383.    lichan = foptst(libfil,"r") ;
  384.    putl("\n/* routines from library file, ",ouchan) ;
  385.    putl(libfil,ouchan) ; putl("*/\n",ouchan) ;
  386.    putl("/****************************************/\n",ouchan) ;
  387.    getctr = 0; quotes = 0; apostrs = 0; combal = 0;
  388.    while (getfu(name) != VOID)
  389.      { if ( !look(name,table1) )
  390.          {putstr("Warning: lib routine, ");
  391.           putstr(name); putstr(", not indexed.\n") ;
  392.           putchar(BELL) ;
  393.          }
  394.        for ( p=fubuff+OFFSET; *p ; p++)  ;
  395.        *p++ = LF ; *p = NULL ;
  396.        if( look(name,table2) )
  397.          {for (p=fubuff+OFFSET ; *p == LF ; p++ )  ;
  398.           deruns(SP,p); pairdec(SP,LF,p) ; deruns(LF,p) ;
  399.           for ( ; *p ; putc(*p++,ouchan) )  ;
  400.           putl("/**/\n",ouchan) ;
  401.          }
  402.      }
  403.   fclose(ouchan) ;
  404.  }
  405. /* open file if possible, error msg & exit if not */
  406. FILE *foptst(file,rw) char *file,*rw ;
  407.  {FILE *chan;
  408.   chan = fopen(file,rw) ;
  409.   if( (chan == NULL) || (chan == VOID) ) ferror(file) ;
  410.   return(chan) ;
  411.  }
  412. /* error msg & exit on errant fopen */
  413. ferror(s) char *s;
  414.  {int i; for (i=1; i < 100; putchar(BELL),i++) ;
  415.   putstr("can't open "); putstr(s); putchar(LF); exit();
  416.  }
  417. /* copy str t to str s */
  418. strcpy(s,t) char *s,*t;
  419.  {while(*s++ = *t++)  ;
  420.  }
  421. /* length of str s, incl. LF if present */
  422. strlen(s) char *s;
  423.  {char *p; p = s; while(*p) p++; return(p-s);
  424.  }
  425. /* get str fm KB, replace LF with NULL at end*/
  426. getstr(a) char *a;
  427.  {int n; n=0;
  428.   if( (*a=getchar()) == VOID) return(VOID) ;
  429.   if( *a == LF ) {*a=NULL; return(NULL);}
  430.   while( (*++a=getchar()) != LF) ++n ;
  431.   *a = NULL; return(n);
  432.  }
  433. /*put str to screen, no auto LF at end*/
  434. putstr(s) char *s;
  435.  {while(*s) putchar(*s++) ;
  436.  }
  437. /* same as putstr, but to chan */
  438. putl(a,chan) char *a; FILE *chan;
  439.  {while(*a) putc(*a++,chan) ;
  440.  }
  441. /* similar to getstr, but from chan, and with LF followed by NULL */
  442. getl(a,chan) char *a; FILE * chan;
  443.  {int n; char c; n=0;
  444.   if( (c = *a++ = getc(chan)) == VOID) return(VOID) ;
  445.   if( c == LF) {*a = NULL; return(ONE) ; }
  446.   while( (*a=getc(chan)) != LF ) ++a,++n ;
  447.   *++a = NULL; return(++n) ;
  448.  }
  449. /* same as getl but with LF replaced by null */
  450. getl0(a,chan) char *a; FILE * chan;
  451.  {int n; n=getl(a,chan) ; if (n == VOID) return(VOID);
  452.   *(a+n) = NULL ; return(n);
  453.  }
  454. /* cmp strs p and q; return -1,0,1, for p<q,p=q,p>q */
  455. strcmp(p,q) char *p,*q;
  456.  {while(*p == *q) {if (*p++ == NULL) return(SAME) ; else ++q; }
  457.   return(*p < *q ? LESS:MORE) ;
  458.  }
  459. /* change runs of c to single c thruout s */
  460. deruns(c,s) char c,*s;
  461.  {pairdec(c,c,s) ;
  462.  }
  463. /* change ab to b thruout s */
  464. pairdec(a,b,s) char a,b,*s;
  465.  {char *t;
  466.   for (t=s; *s = *t; )
  467.     if( (*++t != b) || (*s != a) ) s++ ;
  468.  }
  469. /* print signed dec int -- assumes ASCII */
  470. printd(n) int n;
  471.  {int i;
  472.   if(n < 0) {putchar('-'); n = -n ; }
  473.   if( (i=n/10) != 0) printd(i) ;
  474.   putchar(n % 10 + '0') ;
  475.  }
  476. /*copy 1st n bytes of t to s, insert NULL at end*/
  477. strcpyn(s,t,n) char *s,*t; int n;
  478.  {for ( ; n ; n--) *s++ = *t++ ; *s = NULL ;
  479.  }
  480. /*test if str q is at p. rtrn 1 is so, 0 if not*/
  481. strisat(p,q) char *p,*q ;
  482.  {while( *p && *q && (*q == *p) ) p++,q++ ;
  483.   return(*q ? NULL:ONE) ;
  484.  }
  485. /* get a pos. int from kb */
  486. getint()
  487.  {char c; int n;
  488.   for(n=0; isadig(c=getchar()); ) n=10*n + c - '0' ;
  489.   return(n) ;
  490.  }
  491. /* test if byte is a digit */
  492. isadig(c) char c;
  493.  {return( ((c >= '0') && (c <= '9')) ? ONE:NULL ) ;
  494.  }
  495.  
  496.